package com.zyl.pts.config.advice;

import com.alibaba.fastjson2.JSON;
import com.zyl.pts.config.anno.SystemLog;
import com.zyl.pts.config.exception.ServiceException;
import com.zyl.pts.domain.bean.Account;
import com.zyl.pts.domain.bean.Ip;
import com.zyl.pts.domain.bean.Log;
import com.zyl.pts.mapper.LogMapper;
import com.zyl.pts.utils.CUtil;
import com.zyl.pts.utils.IpUtils;
import com.zyl.pts.utils.ThreadLocalUtil;
import jakarta.servlet.http.HttpServletRequest;
import nl.bitwalker.useragentutils.UserAgent;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;

/**
 * @version v1.0
 * @ProjectName: product_source
 * @Package: com.zyl.pts.config.advice
 * @ClassName: SystemLogAdvice
 * @Description: TODO(系统日志切面)
 * @Author 翟玉龙
 * @Date 2024/11/21-10:18
 */
@Aspect
@Component
public class SystemLogAdvice {

    private final LogMapper logMapper;

    public SystemLogAdvice(LogMapper logMapper) {
        this.logMapper = logMapper;
    }

    /**
     * 定义切点，切入点为SystemLog注解
     */
    @Pointcut("@annotation(com.zyl.pts.config.anno.SystemLog)")
    public void SystemLogPointCut() {

    }

    /**
     * 环绕通知，记录日志
     * @param joinPoint {@link ProceedingJoinPoint }
     * @return {@link Object }
     */
    @Transactional
    @Around("SystemLogPointCut()")
    public Object SystemLogInfo(ProceedingJoinPoint joinPoint) throws Throwable {
        /*
            记录开始时间
         */
        Long startTime = System.currentTimeMillis();

        /*
            获取请求信息
         */
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if(requestAttributes instanceof ServletRequestAttributes) {
            /*
                获取请求对象
             */
            HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();

            /*
                创建日志对象
             */
            Log log = new Log();

            /*
                获取用户名
             */
            Account user = ThreadLocalUtil.get();
            log.setLogUsername(user.getUsername());
            log.setLogRealname(user.getRealname());
            CUtil.logInfo("操作用户：" + user.getRealname());

            /*
                获取注解对象，存储接口描述信息
             */
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            SystemLog systemLog = methodSignature.getMethod().getAnnotation(SystemLog.class);
            CUtil.logInfo("接口描述：" + systemLog.business());

            /*
                获取请求接口
             */
            log.setLogRequestUri(request.getRequestURI());
            CUtil.logInfo("请求接口：" + request.getRequestURL().toString());

            /*
                获取请求方法
             */
            log.setLogRequestMethod(request.getMethod());
            CUtil.logInfo("请求方法：" + request.getMethod());

            // 过滤 MultipartFile 对象
            StringBuilder argsStringBuilder = new StringBuilder();
            boolean hasMultipartFile = false;

            for (Object arg : joinPoint.getArgs()) {
                if (arg instanceof MultipartFile file) {
                    argsStringBuilder.append("File name: ").append(file.getOriginalFilename())
                            .append(", File size: ").append(file.getSize()).append(" bytes");
                    hasMultipartFile = true;
                }
            }

            /*
                获取请求参数
             */
            if (hasMultipartFile) {
                CUtil.logInfo("请求参数：" + argsStringBuilder);
            } else {
                CUtil.logInfo("请求参数：" + JSON.toJSONString(joinPoint.getArgs()));
            }

            /*
                获取类地址
             */
            CUtil.logInfo("类地址：" + joinPoint.getSignature().getDeclaringTypeName());

            /*
                分析请求头User-Agent
             */
            UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));

            /*
                读取系统信息
             */
            CUtil.logInfo("系统名称：" + userAgent.getOperatingSystem().getName());

            /*
                读取浏览器信息，如果是用测试工具的，那么判断并处理
             */
            String browserName;
            browserName = "Unknown".equals(userAgent.getOperatingSystem().getName()) ?
                    "Api测试工具" : userAgent.getBrowser().getName();
            CUtil.logInfo("浏览器名称：" + browserName);

            /*
                获取真实IP
             */
            Ip ip = IpUtils.getIpByRequest(request);
            log.setLogHost(ip.getIp());
            CUtil.logInfo("真实IP：" + log.getLogHost());

            /*
                网络运营商
             */
            CUtil.logInfo("网络运营商：" + ip.getAddr());

            /*
                调用核心方法 ---> 分割上下部分，上方获取基本信息，下方获取方法执行后信息
             */
            Object proceed = joinPoint.proceed();

            /*
                响应参数
             */
            CUtil.logInfo("响应参数：" + JSON.toJSONString(proceed));

            /*
                创建时间
             */
            log.setLogTime(LocalDateTime.now());
            // 时间格式 yyyy-MM-dd HH:mm:ss

            CUtil.logInfo("创建时间：" + CUtil.formatLocalDateTime());

            /*
                记录结束时间
             */
            Long endTime = System.currentTimeMillis();
            CUtil.logInfo("执行耗时：" + (endTime - startTime) + "ms");

            /*
                记录日志
             */
            logMapper.insertLog(log);
            return proceed;
        } else {
            // 如果没有请求对象，可能是非HTTP环境，需要特殊处理
            throw new ServiceException("无法获取请求对象，请检查环境配置");
        }
    }
}
